package com.itbaizhan.shopping_search_service.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.query_dsl.*;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.CompletionSuggestOption;
import co.elastic.clients.elasticsearch.core.search.FieldSuggester;
import co.elastic.clients.elasticsearch.core.search.Suggester;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.elasticsearch.indices.AnalyzeRequest;
import co.elastic.clients.elasticsearch.indices.AnalyzeResponse;
import co.elastic.clients.elasticsearch.indices.analyze.AnalyzeToken;
import co.elastic.clients.json.JsonData;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.itbaizhan.shopping_common.pojo.*;
import com.itbaizhan.shopping_common.service.SearchService;
import com.itbaizhan.shopping_search_service.repository.GoodsESRepository;
import lombok.SneakyThrows;
import org.apache.dubbo.config.annotation.DubboService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.client.elc.NativeQuery;
import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;

@DubboService
public class SearchServiceImpl implements SearchService {
    @Autowired
    private ElasticsearchClient elasticsearchClient;

    /**
     * 分词
     *
     * @param text     待分词的文本
     * @param analyzer 分词器
     * @return 分词后的结果
     */
    public List<String> analyze(String text, String analyzer) throws IOException {
        //创建分词请求
        AnalyzeRequest request = new AnalyzeRequest.Builder()
                .index("goods")
                .analyzer(analyzer)
                .text(text)
                .build();
        //执行分词
        AnalyzeResponse analyze = elasticsearchClient.indices().analyze(request);
        //处理分词结果
        List<String> words = new ArrayList<>();
        List<AnalyzeToken> tokens = analyze.tokens();
        for (AnalyzeToken token : tokens) {
            words.add(token.token());
        }
        return words;
    }

    @Override
    @SneakyThrows
    public List<String> autoSuggest(String keyword) {
        // 1.构造自动补齐查询条件
        Suggester suggester = Suggester.of(
                s -> s.suggesters("prefix_suggestion", FieldSuggester.of( //prefix_suggestion：查询名
                        fs -> fs.completion(
                                cs -> cs.skipDuplicates(true) //去重
                                        .size(10)
                                        .field("tags") //匹配的字段名
                        )
                )).text(keyword) //当前需要补齐的字符
        );


        // 2.自动补齐查询
        SearchResponse<Map> response = elasticsearchClient.search(s -> s.index("goods")
                .suggest(suggester), Map.class);


        // 3.处理查询结果
        Map resultMap = response.suggest();
        List<Suggestion> suggestionList = (List) resultMap.get("prefix_suggestion");
        Suggestion suggestion = suggestionList.get(0);
        List<CompletionSuggestOption> resultList = suggestion.completion().options();


        List<String> result = new ArrayList<>();
        for (CompletionSuggestOption completionSuggestOption : resultList) {
            String text = completionSuggestOption.text();
            result.add(text);
        }
        return result;
    }

    @Autowired
    private ElasticsearchTemplate elasticsearchTemplate;

    /**
     * 搜索
     * @param goodsSearchParam 搜索条件
     * @return 搜索结果
     */
    @Override
    public GoodsSearchResult search(GoodsSearchParam goodsSearchParam) {
        // 1.构造ES搜索条件
        NativeQuery nativeQuery = buildQuery(goodsSearchParam);
        // 2.搜索
        SearchHits<GoodsES> searchHits = elasticsearchTemplate.search(nativeQuery, GoodsES.class);
        // 3.将查询结果封装为Page对象
        // 3.1 将SearchHits转为List
        List<GoodsES> content = new ArrayList<>();
        for (SearchHit<GoodsES> searchHit : searchHits) {
            GoodsES goodsES = searchHit.getContent();
            content.add(goodsES);
        }
        // 3.2 将List转为MP的Page对象
        Page<GoodsES> goodsESPage = new Page<>();
        goodsESPage.setCurrent(goodsSearchParam.getPage());//当前页
        goodsESPage.setSize(goodsSearchParam.getSize());//每页条数
        goodsESPage.setRecords(content);//结果集
        goodsESPage.setTotal(searchHits.getTotalHits());//总条数
        // 4.封装结果对象
        // 4.1 查询结果
        GoodsSearchResult result = new GoodsSearchResult();
        result.setGoodsPage(goodsESPage);
        // 4.2 查询参数
        result.setGoodsSearchParam(goodsSearchParam);
        // 4.3 查询面板
        buildPanel(result);
        return result;
    }


    private void buildPanel(GoodsSearchResult result) {
        List<GoodsES> goodsESList = result.getGoodsPage().getRecords();

         Set<String> brands = new HashSet<>(); // 和商品有关的品牌列表
         Set<String> productType = new HashSet<>(); // 和商品有关的类别列表
        // 和商品有关的规格列表，键：规格名，值：规格集合
         Map<String, Set<String>> specifications = new HashMap<>();
        for (int i = 0; i < Math.min(20,goodsESList.size()); i++) {
            brands.add(goodsESList.get(i).getBrand());
            productType.addAll(goodsESList.get(i).getProductType());
            Map<String, List<String>> specification = goodsESList.get(i).getSpecification();
            for (String key : specification.keySet()) {
                List<String> value = specification.get(key);
                specifications.computeIfAbsent(key , k -> new HashSet<>()).addAll(value);
            }
        }
        result.setBrands(brands);
        result.setProductType(productType);
        result.setSpecifications(specifications);
    }

    /**
     * 构造搜索条件
     *
     * @param goodsSearchParam 查询条件对象
     * @return 搜索条件对象
     */
    private NativeQuery buildQuery(GoodsSearchParam goodsSearchParam) {
        // 1、构造复杂搜索条件对象
        NativeQueryBuilder nativeQueryBuilder = new NativeQueryBuilder();
        BoolQuery.Builder builder = new BoolQuery.Builder();
        // 2、如果查询条件有关键词，关键词可以匹配品牌、商品名、副标题字段；否则查询所有
        if (StringUtils.hasText(goodsSearchParam.getKeyword())) {
            MultiMatchQuery keywordQuery = MultiMatchQuery.of(q -> q.query(goodsSearchParam.getKeyword()).fields("goodsName", "caption", "brand"));
            builder.must(keywordQuery._toQuery());
        } else {
            MatchAllQuery allQuery = new MatchAllQuery.Builder().build();
            builder.must(allQuery._toQuery());
        }
        // 3、如果查询条件有品牌名，则精准匹配品牌
        if (StringUtils.hasText(goodsSearchParam.getBrand())) {
            MatchQuery brandQuery = MatchQuery.of(q -> q.field("brand").query(goodsSearchParam.getBrand()));
            builder.must(brandQuery._toQuery());
        }
        // 4、如果有价格，则范围匹配价格
        Double highPrice = goodsSearchParam.getHighPrice();
        Double lowPrice = goodsSearchParam.getLowPrice();
        if(highPrice!=null && highPrice != 0){
            RangeQuery highQuery = RangeQuery.of(q -> q.field("price").lte(JsonData.of(highPrice)));
            builder.must(highQuery._toQuery());
        }
        if(lowPrice!=null ){
            RangeQuery lowQuery = RangeQuery.of(q -> q.field("price").gte(JsonData.of(lowPrice)));
            builder.must(lowQuery._toQuery());
        }
        // 5、如果查询条件有规格选项，则精确匹配规格选项
        Map<String, String> specificationOptions = goodsSearchParam.getSpecificationOption();
        if(specificationOptions!=null && specificationOptions.size()>0){
            Set<Map.Entry<String, String>> entries = specificationOptions.entrySet();
            for (Map.Entry<String, String> entry : entries) {
                String key = entry.getKey();
                String value = entry.getValue();
                if(StringUtils.hasText(key)){
                    TermQuery termQuery = TermQuery.of(q -> q.field("specification." + key + ".keyword").value(value));
                    builder.must(termQuery._toQuery());
                }
            }
        }
        nativeQueryBuilder.withQuery(builder.build()._toQuery());
        // 6、添加分页条件
        PageRequest pageRequest = PageRequest.of(goodsSearchParam.getPage()-1,goodsSearchParam.getSize());
        nativeQueryBuilder.withPageable(pageRequest);
        // 7、如果查询条件有排序，则添加排序条件
        if(StringUtils.hasText(goodsSearchParam.getSortFiled())){
            String sortFiled = goodsSearchParam.getSortFiled();
            String sort = goodsSearchParam.getSort();
            if(StringUtils.hasText(sortFiled) && StringUtils.hasText(sort)){
                Sort sortParam = null;
                if(sortFiled.equals("NEW")){
                    if(sort.equals("ASC")){
                        sortParam = Sort.by(Sort.Direction.DESC,"id");
                    }
                    if(sort.equals("DESC")){
                        sortParam = Sort.by(Sort.Direction.ASC,"id");
                    }
                }
                if(sortFiled.equals("PRICE")){
                    if(sort.equals("ASC")){
                        sortParam = Sort.by(Sort.Direction.ASC,"price");
                    }
                    if(sort.equals("DESC")){
                        sortParam = Sort.by(Sort.Direction.DESC,"price");
                    }
                }
                nativeQueryBuilder.withSort(sortParam);
            }
        }
        // 8、返回搜索条件对象
        return nativeQueryBuilder.build();

    }





    @Autowired
    private GoodsESRepository goodsESRepository;

    @Override
    @SneakyThrows
    public void syncGoodsToEs(GoodsDesc goodsDesc) {
        // 将商品详情对象转换为GoodsES对象
        GoodsES goodsES = new GoodsES();
        goodsES.setId(goodsDesc.getId());
        goodsES.setGoodsName(goodsDesc.getGoodsName());
        goodsES.setCaption(goodsDesc.getCaption());
        goodsES.setPrice(goodsDesc.getPrice());
        goodsES.setHeaderPic(goodsDesc.getHeaderPic());
        goodsES.setBrand(goodsDesc.getBrand().getName());
        List<String> productType = new ArrayList<>();
        productType.add(goodsDesc.getProductType1().getName());
        productType.add(goodsDesc.getProductType2().getName());
        productType.add(goodsDesc.getProductType3().getName());
        goodsES.setProductType(productType);
        List<String> tags = new ArrayList<>();
        tags.add(goodsDesc.getBrand().getName()); //品牌名是关键字
        List<String> analyze = analyze(goodsDesc.getGoodsName(), "ik_smart");
        for (String word : analyze) {
            tags.add(word);
        }
        List<String> analyze1 = analyze(goodsDesc.getCaption(), "ik_smart");
        for (String word : analyze1) {
            tags.add(word);
        }
        goodsES.setTags(tags);
        //规格
        Map<String, List<String>> specification = new HashMap<>();
        List<Specification> specifications = goodsDesc.getSpecifications();
        for (Specification specification1 : specifications) {
            specification.put(specification1.getSpecName(), new ArrayList<>());
            for (SpecificationOption specificationOption : specification1.getSpecificationOptions()) {
                specification.get(specification1.getSpecName()).add(specificationOption.getOptionName());
            }
        }
        goodsES.setSpecification(specification);
        //保存到es中
        goodsESRepository.save(goodsES);
    }

    @Override
    public void delete(Long id) {
        goodsESRepository.deleteById(id);
    }
}
